﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Raider.WinForms.Actions.Design
{
    /// <summary>
    /// Extended TreeView with reduced flickering and support of nodes
    /// without images.
    /// </summary>
    sealed class BufferedTreeView : TreeView
    {
        public int NoneImageIndex
        {
            get; set;
        }

        public BufferedTreeView()
        {
            LineColor = SystemColors.GrayText;
            DrawMode = TreeViewDrawMode.OwnerDrawAll;
        }

        protected override void OnHandleCreated(EventArgs e)
        {
            // TVM_SETEXTENDEDSTYLE, TVS_EX_DOUBLEBUFFER.
            SendMessage(Handle, 0x112C, (IntPtr)0x0004, (IntPtr)0x0004);
            base.OnHandleCreated(e);
        }

        [DllImport("user32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

        protected override void OnDrawNode(DrawTreeNodeEventArgs e)
        {
            const int SPACE_IL = 3; // space between Image and Label
            
            // we only do additional drawing
            e.DrawDefault = true;

            base.OnDrawNode(e);

            if (ShowLines && ImageList != null && e.Node.ImageIndex == NoneImageIndex)
            {
                // Image size
                int imgW = ImageList.ImageSize.Width;
                int imgH = ImageList.ImageSize.Height;

                // Image center
                int xPos = e.Node.Bounds.Left - SPACE_IL - imgW / 2;
                int yPos = (e.Node.Bounds.Top + e.Node.Bounds.Bottom) / 2;

                // Image rect
                Rectangle imgRect = new Rectangle(xPos, yPos, 0, 0);
                imgRect.Inflate(imgW / 2, imgH / 2);

                using (Pen p = new Pen(LineColor, 1))
                {
                    p.DashStyle = DashStyle.Dot;

                    // account uneven Indent for both lines
                    p.DashOffset = Indent % 2;

                    // Horizontal treeline across width of image
                    // account uneven half of delta ItemHeight & ImageHeight
                    int yHor = yPos + ((ItemHeight - imgRect.Height) / 2) % 2;

                    e.Graphics.DrawLine(p,
                                        (ShowRootLines || e.Node.Level > 0)
                                            ? imgRect.Left
                                            : xPos - (int)p.DashOffset,
                                        yHor, imgRect.Right, yHor);

                    if (!CheckBoxes && e.Node.IsExpanded)
                    {
                        // Vertical treeline , offspring from NodeImage center to e.Node.Bounds.Bottom
                        // yStartPos: account uneven Indent and uneven half of delta ItemHeight & ImageHeight
                        int yVer = yHor + (int)p.DashOffset;
                        e.Graphics.DrawLine(p, xPos, yVer, xPos, e.Node.Bounds.Bottom);
                    }
                }
            }
        }

        protected override void OnAfterCollapse(TreeViewEventArgs e)
        {
            base.OnAfterCollapse(e);

            if (!CheckBoxes && ImageList != null && e.Node.ImageIndex == NoneImageIndex)
            {
                // DrawNode event not raised: redraw node with collapsed treeline
                Invalidate(e.Node.Bounds);
            }
        }
    }
}